package com.free.file.utils;

import com.alibaba.csp.sentinel.util.StringUtil;
import com.jcraft.jsch.*;
import org.apache.commons.io.FileUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;

/**
 * java 使用jsch远程链接linux 执行命令
 *
 * @author Weikisa
 */
public class JSchUtil {
    private static JSch jsch;//定义jsch连接类
    private static Session session;
    private static String user = "root";//Linux系统的用户名
    private static String password = "9464429743";//密码
    private static String host = "192.168.2.77";//測試IP，實際使用以輸入IP地址為准

    /**
     * 首先连接服务器
     *
     * @throws JSchException
     */
    public static Session connect(String user, String hostIP, String password, Integer port) throws JSchException {
        jsch = new JSch();//将jsch实例化
        Session session = jsch.getSession(user, hostIP, port);//默认端口号为22端口，使用用户名user连接IP
        session.setPassword(password);//设置登录密码
        session.setConfig("StrictHostKeyChecking" , "no" );//是否使用密钥登录，一般默认为no
        session.connect(60 * 1000);
        return session;
    }

    /**
     * 连接服务器后执行相应的linux命令
     *
     * @param command
     * @throws JSchException
     */
    public static boolean execCmd(Session session, String command) throws JSchException {
//        connect(hostIP);//连接指定IP地址的Linux
        BufferedReader reader = null;
        Channel channel = null;//定义流通道
        try {
            while (command != null) {
                channel = session.openChannel("exec" );
                ((ChannelExec) channel).setCommand(command);
                channel.setInputStream(null);
                ((ChannelExec) channel).setErrStream(System.err);
                // 获取流
                InputStream in = channel.getInputStream();//定义输入的通道
                channel.connect();//连接

                byte[] tmp = new byte[1024];


                while (true) {
                    while (in.available() > 0) {
                        int i = in.read(tmp, 0, 1024);
                        if (i < 0) break;
                        System.out.print(new String(tmp, 0, i));
                    }
                    if (channel.isClosed()) {
                        if (in.available() > 0) continue;
                        System.out.println("exit-status: " + channel.getExitStatus());
                        if (channel.getExitStatus() != 0) {
                            return false;
                        }
                        break;
                    }
//		            try {
//		                Thread.sleep(1000);
//		            } catch (Exception ee) {
//		            }
                }
                channel.disconnect();
                session.disconnect();
                System.out.println("Linux指令執行結束" );
                return true;
            }
        } catch (IOException | JSchException e) {
            e.printStackTrace();
        }
        return true;
    }

    /**
     * ChannelSftp下载目录下所有或指定文件
     * ChannelSftp获取某目录下所有文件名称
     * InputStream转File
     */
    public static Map<String, File> downFile(Session sshSession, String remoteDir) {
        Map<String, File> fileMap = new HashMap<>(20);
        if (StringUtil.isEmpty(remoteDir)) {
            return fileMap;
        }
        ChannelSftp channelSftp = null;
        try {
            Properties sshConfig = new Properties();
            //当第一次连接服务器时，自动接受新的公钥
            sshConfig.put("StrictHostKeyChecking" , "no" );
            sshSession.setConfig(sshConfig);
            Channel channel = sshSession.openChannel("sftp" );
            channel.connect();
            channelSftp = (ChannelSftp) channel;

            //ls命令获取文件名列表
            Vector vector = channelSftp.ls(remoteDir);
            Iterator iterator = vector.iterator();
            while (iterator.hasNext()) {
                ChannelSftp.LsEntry file = (ChannelSftp.LsEntry) iterator.next();
                //文件名称
                String fileName = file.getFilename();
                //todo 这里可使用if或正则不下载一些文件
                try {
                    InputStream inputStream = channelSftp.get(remoteDir);
                    System.out.println("开始下载文件：" + fileName);
                    if (inputStream != null) {
                        File newFile = new File(fileName);
                        //将InputStream转File
                        FileUtils.copyToFile(inputStream, newFile);
                        fileMap.put(fileName, newFile);
                    }
                } catch (Exception e) {
                    System.out.println("获取文件失败：" + fileName);
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (channelSftp != null) {
                channelSftp.exit();
            }
        }
        return fileMap;
    }

    public static String getFileString(Session sshSession, String remoteDir) {
        if (StringUtil.isEmpty(remoteDir)) {
            return "";
        }
        ChannelSftp channelSftp = null;
        try {
            Properties sshConfig = new Properties();
            //当第一次连接服务器时，自动接受新的公钥
            sshConfig.put("StrictHostKeyChecking" , "no" );
            sshSession.setConfig(sshConfig);
            Channel channel = sshSession.openChannel("sftp" );
            channel.connect();
            channelSftp = (ChannelSftp) channel;

            //ls命令获取文件名列表
            Vector vector = channelSftp.ls(remoteDir);
            Iterator iterator = vector.iterator();
            while (iterator.hasNext()) {
                ChannelSftp.LsEntry file = (ChannelSftp.LsEntry) iterator.next();
                //文件名称
                String fileName = file.getFilename();
                //todo 这里可使用if或正则不下载一些文件
                try {
                    InputStream inputStream = channelSftp.get(remoteDir);
                    System.out.println("文件读取：" + fileName);
                    if (inputStream != null) {
                        //将InputStream转File
                        return new String(inputStream.readAllBytes());
                    }
                } catch (Exception e) {
                    System.out.println("获取文件失败：" + fileName);
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (channelSftp != null) {
                channelSftp.exit();
            }
        }
        return "";
    }

    public Set<String> listFile(Session sshSession, String remoteDir) {
        Set<String> set = new HashSet<>();
        if (StringUtil.isEmpty(remoteDir)) {
            return set;
        }
        ChannelSftp channelSftp = null;
        try {
            Properties sshConfig = new Properties();
            //当第一次连接服务器时，自动接受新的公钥
            sshConfig.put("StrictHostKeyChecking" , "no" );
            sshSession.setConfig(sshConfig);
            Channel channel = sshSession.openChannel("sftp" );
            channel.connect();
            channelSftp = (ChannelSftp) channel;

            //ls命令获取文件名列表
            Vector vector = channelSftp.ls(remoteDir);
            Iterator iterator = vector.iterator();
            while (iterator.hasNext()) {
                ChannelSftp.LsEntry file = (ChannelSftp.LsEntry) iterator.next();
                //文件名称
                String fileName = file.getFilename();
                //todo 这里可使用if或正则不下载一些文件
                try {
                    InputStream inputStream = channelSftp.get(remoteDir + "/" + fileName);
                    if (inputStream != null) {
                        //将InputStream转File
                        set.add(fileName);
                    }
                } catch (Exception e) {
                    System.out.println("获取文件失败：" + fileName);
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (channelSftp != null) {
                channelSftp.exit();
            }
        }
        return set;
    }

    public static void uploadFile(ChannelSftp channel, String srcFilePath, String destFilePath) {
        File file = new File(srcFilePath);
        String subDestFilePath = destFilePath + "/" + file.getName(); // 真·文件目录
        // 1. 源文件为目录, 则列表显示其下所有的文件夹、文件
        if (file.isDirectory()) {
            // 1.1 在destFilePath目录下检查本目录是否存在, 存在则删除再创建; 不存在则新创建
            try {
                channel.cd(subDestFilePath);
                System.out.println("目标服务器上 目录: " + subDestFilePath + " 存在, 先予以删除" );
                channel.cd(destFilePath);
                deleteTargetFile(channel, subDestFilePath);
                System.out.println("目标服务器上 目录: " + subDestFilePath + " 删除成功" );
                channel.mkdir(subDestFilePath);
                System.out.println("目标服务器上 目录: " + subDestFilePath + " 删除后创建成功" );
            } catch (SftpException e) {
                System.out.println("目标服务器上 目录: " + subDestFilePath + " 不存在, 正在创建" );
                try {
                    channel.mkdir(subDestFilePath);
                } catch (SftpException e1) {
                    System.out.println("目标服务器上 创建目录: " + subDestFilePath + " 出错." + e1.getLocalizedMessage());
                }
            }
            File subfiles[] = file.listFiles();
            for (int i = 0; i < subfiles.length; i++) {
                uploadFile(channel, subfiles[i].getAbsolutePath(), subDestFilePath);
            }
        } else {

            try {
                if (!channel.isConnected()) {
                    channel.connect();
                }
                Class cl = ChannelSftp.class;
                Field field = cl.getDeclaredField("server_version" );
                field.setAccessible(true);
                field.set(channel, 2);
                channel.setFilenameEncoding("GBK" );
//                channel.setFilenameEncoding("GBK");
                channel.cd(destFilePath);
                channel.put(srcFilePath, destFilePath + "/" + file.getName(), ChannelSftp.OVERWRITE);
                System.out.println("传输文件: " + srcFilePath + "  到  " + destFilePath + "/" + file.getName() + "  成功." );
            } catch (SftpException e) {
                System.out.println("传输文件: " + srcFilePath + "  到  " + destFilePath + "/" + file.getName() + "  出错."
                        + e.getLocalizedMessage());
            } catch (JSchException | IllegalAccessException | NoSuchFieldException e) {
                throw new RuntimeException(e);
            }
        }

    }

    public static void deleteTargetFile(ChannelSftp channel, String targetFilePath) {
        try {
            // 1. 路径为文件夹
            if (channel.stat(targetFilePath).isDir()) {
                channel.cd(targetFilePath);
                Vector<ChannelSftp.LsEntry> entries = channel.ls("." );
                for (ChannelSftp.LsEntry subFilePath : entries) {
                    String fileName = subFilePath.getFilename();
                    if (".".equals(fileName) || "..".equals(fileName)) {
                        continue;
                    }
                    deleteTargetFile(channel, subFilePath.getFilename());
                }
                channel.cd(".." );
                channel.rmdir(targetFilePath);
                System.out.println("删除文件夹: " + targetFilePath + " 成功. " );
            } else {
                // 2. 路径为文件
                try {
                    channel.rm(targetFilePath);
                    System.out.println("删除文件: " + targetFilePath + " 成功. " );
                } catch (SftpException e) {
                    System.out.println("删除文件: " + targetFilePath + " 出错. " + e.getMessage());
                }
            }
        } catch (SftpException e) {
            System.out.println("判断文件: " + targetFilePath + " 类型出错. " + e.getMessage());
        }

    }
}
